package org.dromara.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcel;
import cn.idev.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.dp.vo.NodeMapVo;
import org.dromara.dp.vo.NodeRelationVo;
import org.dromara.dp.vo.NodeVo;
import org.dromara.system.domain.AlarmIndicator;
import org.dromara.system.domain.CriticalAlarmRiskEvent;
import org.dromara.system.domain.Equipment;
import org.dromara.system.domain.bo.CriticalAlarmRiskEventBo;
import org.dromara.system.domain.vo.CommonExcelVo;
import org.dromara.system.domain.vo.CriticalAlarmRiskEventImportVo;
import org.dromara.system.domain.vo.CriticalAlarmRiskEventVo;
import org.dromara.system.mapper.AlarmIndicatorMapper;
import org.dromara.system.mapper.CriticalAlarmRiskEventMapper;
import org.dromara.system.mapper.EquipmentMapper;
import org.dromara.system.service.ICriticalAlarmRiskEventService;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Node;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 关键报警风险事件Service业务层处理
 *
 * @author Lion Li
 * @date 2025-10-29
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class CriticalAlarmRiskEventServiceImpl implements ICriticalAlarmRiskEventService {

    private final CriticalAlarmRiskEventMapper baseMapper;

    private final EquipmentMapper equipmentMapper;

    private final AlarmIndicatorMapper alarmIndicatorMapper;

    /**
     * 查询关键报警风险事件
     *
     * @param id 主键
     * @return 关键报警风险事件
     */
    @Override
    public CriticalAlarmRiskEventVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询关键报警风险事件列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 关键报警风险事件分页列表
     */
    @Override
    public TableDataInfo<CriticalAlarmRiskEventVo> queryPageList(CriticalAlarmRiskEventBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<CriticalAlarmRiskEvent> lqw = buildQueryWrapper(bo);
        Page<CriticalAlarmRiskEventVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<CriticalAlarmRiskEventVo> list = result.getRecords();
        if (!list.isEmpty()) {
            List<Long> equipmentIds = list.stream().map(CriticalAlarmRiskEventVo::getEquipmentId).toList();
            List<Equipment> equipmentList = equipmentMapper.selectByIds(equipmentIds);
            Map<Long, String> map = equipmentList.stream().collect(Collectors.toMap(Equipment::getId, Equipment::getSbmc));
            list.forEach(vo -> {
                vo.setEquipmentName(map.get(vo.getEquipmentId()));
                if (vo.getInherentRiskSeverity() != null) {
                    vo.setInherentRiskSeverityName(String.valueOf((char) (vo.getResidualRiskSeverity() + 'A')));
                }
                if (vo.getResidualRiskSeverity() != null) {
                    vo.setResidualRiskSeverityName(String.valueOf((char) (vo.getResidualRiskSeverity() + 'A')));
                }
            });
        }
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的关键报警风险事件列表
     *
     * @param bo 查询条件
     * @return 关键报警风险事件列表
     */
    @Override
    public List<CriticalAlarmRiskEventVo> queryList(CriticalAlarmRiskEventBo bo) {
        LambdaQueryWrapper<CriticalAlarmRiskEvent> lqw = buildQueryWrapper(bo);
        List<CriticalAlarmRiskEventVo> list = baseMapper.selectVoList(lqw);
        if (!list.isEmpty()) {
            List<Long> equipmentIds = list.stream().map(CriticalAlarmRiskEventVo::getEquipmentId).toList();
            List<Equipment> equipmentList = equipmentMapper.selectByIds(equipmentIds);
            Map<Long, String> map = equipmentList.stream().collect(Collectors.toMap(Equipment::getId, Equipment::getSbmc));
            list.forEach(vo -> {
                vo.setEquipmentName(map.get(vo.getEquipmentId()));
                if (vo.getInherentRiskSeverity() != null) {
                    vo.setInherentRiskSeverityName(String.valueOf((char) (vo.getResidualRiskSeverity() + 'A')));
                }
                if (vo.getResidualRiskSeverity() != null) {
                    vo.setResidualRiskSeverityName(String.valueOf((char) (vo.getResidualRiskSeverity() + 'A')));
                }
            });
        }
        return list;
    }

    private LambdaQueryWrapper<CriticalAlarmRiskEvent> buildQueryWrapper(CriticalAlarmRiskEventBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<CriticalAlarmRiskEvent> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(CriticalAlarmRiskEvent::getId);
        lqw.like(StringUtils.isNotBlank(bo.getEventName()), CriticalAlarmRiskEvent::getEventName, bo.getEventName());
        lqw.eq(StringUtils.isNotBlank(bo.getBias1()), CriticalAlarmRiskEvent::getBias1, bo.getBias1());
        lqw.eq(StringUtils.isNotBlank(bo.getBias2()), CriticalAlarmRiskEvent::getBias2, bo.getBias2());
        lqw.eq(StringUtils.isNotBlank(bo.getBias3()), CriticalAlarmRiskEvent::getBias3, bo.getBias3());
        lqw.eq(StringUtils.isNotBlank(bo.getBias4()), CriticalAlarmRiskEvent::getBias4, bo.getBias4());
        lqw.eq(bo.getEquipmentId() != null, CriticalAlarmRiskEvent::getEquipmentId, bo.getEquipmentId());
        lqw.eq(bo.getResidualRiskLikelihood() != null, CriticalAlarmRiskEvent::getInherentRiskLikelihood, bo.getInherentRiskLikelihood());
        lqw.eq(bo.getInherentRiskSeverity() != null, CriticalAlarmRiskEvent::getInherentRiskSeverity, bo.getInherentRiskSeverity());
        lqw.eq(bo.getResidualRiskSeverity() != null, CriticalAlarmRiskEvent::getResidualRiskSeverity, bo.getResidualRiskSeverity());
        lqw.eq(bo.getInherentRiskLikelihood() != null, CriticalAlarmRiskEvent::getInherentRiskLikelihood, bo.getInherentRiskLikelihood());
        lqw.eq(StringUtils.isNotBlank(bo.getReason()), CriticalAlarmRiskEvent::getReason, bo.getReason());
        lqw.eq(StringUtils.isNotBlank(bo.getMeasure()), CriticalAlarmRiskEvent::getMeasure, bo.getMeasure());
        lqw.eq(StringUtils.isNotBlank(bo.getIndicatorNum1()), CriticalAlarmRiskEvent::getIndicatorNum1, bo.getIndicatorNum1());
        lqw.eq(StringUtils.isNotBlank(bo.getIndicatorNum2()), CriticalAlarmRiskEvent::getIndicatorNum2, bo.getIndicatorNum2());
        lqw.eq(StringUtils.isNotBlank(bo.getIndicatorNum3()), CriticalAlarmRiskEvent::getIndicatorNum3, bo.getIndicatorNum3());
        lqw.eq(StringUtils.isNotBlank(bo.getIndicatorNum4()), CriticalAlarmRiskEvent::getIndicatorNum4, bo.getIndicatorNum4());
        lqw.eq(StringUtils.isNotBlank(bo.getResult()), CriticalAlarmRiskEvent::getResult, bo.getResult());
        return lqw;
    }

    /**
     * 新增关键报警风险事件
     *
     * @param bo 关键报警风险事件
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(CriticalAlarmRiskEventBo bo) {
        CriticalAlarmRiskEvent add = MapstructUtils.convert(bo, CriticalAlarmRiskEvent.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改关键报警风险事件
     *
     * @param bo 关键报警风险事件
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(CriticalAlarmRiskEventBo bo) {
        CriticalAlarmRiskEvent update = MapstructUtils.convert(bo, CriticalAlarmRiskEvent.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(CriticalAlarmRiskEvent entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除关键报警风险事件信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public void downloadTemplate(HttpServletResponse response) {
        try {
            //获取模板地址
            ClassPathResource classPathResource = new ClassPathResource("/template/critical_alarm.xlsx");
            //将数据写出指定的sheet页中
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码
            String encodedFileName = URLEncoder.encode("关键链路报警导入模板.xlsx", StandardCharsets.UTF_8);
            response.setHeader("Content-disposition", "attachment;filename=" + encodedFileName);
            ExcelWriter excelWriter = FastExcel.write(response.getOutputStream()).withTemplate(classPathResource.getInputStream()).build();
            //写入不同sheet

            WriteSheet equipmentWriteSheet = FastExcel.writerSheet("设备").head(CommonExcelVo.class).needHead(false).build();
            LambdaQueryWrapper<Equipment> equipmentQueryWrapper = new LambdaQueryWrapper<>();
            equipmentQueryWrapper.eq(Equipment::getDelFlag, 0);
            List<Equipment> equipmentList = equipmentMapper.selectList(equipmentQueryWrapper);
            List<CommonExcelVo> equipmentExelVo = equipmentList.stream().map(p -> {
                CommonExcelVo commonExcelVo = new CommonExcelVo();
                commonExcelVo.setName(p.getSbmc());
                return commonExcelVo;
            }).collect(Collectors.toList());
            excelWriter.write(equipmentExelVo, equipmentWriteSheet);

            WriteSheet indicatorSheet = FastExcel.writerSheet("工艺位号").head(CommonExcelVo.class).needHead(false).build();
            LambdaQueryWrapper<AlarmIndicator> indicatorQueryWrapper = new LambdaQueryWrapper<>();
            indicatorQueryWrapper.eq(AlarmIndicator::getDelFlag, 0);
            List<AlarmIndicator> indicators = alarmIndicatorMapper.selectList(indicatorQueryWrapper);
            List<CommonExcelVo> indicatorVos = indicators.stream().map(p -> {
                CommonExcelVo commonExcelVo = new CommonExcelVo();
                commonExcelVo.setName(p.getSysNum());
                return commonExcelVo;
            }).collect(Collectors.toList());
            excelWriter.write(indicatorVos, indicatorSheet);
            excelWriter.finish();
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    @Transactional
    public Boolean importData(MultipartFile file) {
        try {
            List<Equipment> equipmentList = equipmentMapper.selectList();
            Map<String, Long> equipmentMap = equipmentList.stream().collect(Collectors.toMap(Equipment::getSbmc, Equipment::getId));
            List<CriticalAlarmRiskEventImportVo> vos = ExcelUtil.importExcel(file.getInputStream(), CriticalAlarmRiskEventImportVo.class);
            List<CriticalAlarmRiskEvent> res = vos.stream().map(vo -> {
                CriticalAlarmRiskEvent event = new CriticalAlarmRiskEvent();
                event.setEventName(vo.getEventName());
                event.setBias1(vo.getBias1());
                event.setBias2(vo.getBias2());
                event.setBias3(vo.getBias3());
                event.setBias4(vo.getBias4());
                event.setEquipmentId(equipmentMap.get(vo.getEquipmentName()));
                event.setInherentRiskLikelihood(vo.getInherentRiskLikelihood());
                if (vo.getInherentRiskSeverity() != null) {
                    event.setInherentRiskSeverity((long) (vo.getInherentRiskSeverity().charAt(0) - 'A'));
                }
                event.setResidualRiskLikelihood(vo.getResidualRiskLikelihood());
                if (vo.getResidualRiskSeverity() != null) {
                    event.setResidualRiskSeverity((long) (vo.getResidualRiskSeverity().charAt(0) - 'A'));
                }
                event.setReason(vo.getReason());
                event.setMeasure(vo.getMeasure());
                event.setIndicatorNum1(vo.getIndicatorNum1());
                event.setIndicatorNum2(vo.getIndicatorNum2());
                event.setIndicatorNum3(vo.getIndicatorNum3());
                event.setIndicatorNum4(vo.getIndicatorNum4());
                event.setResult(vo.getResult());
                return event;
            }).collect(Collectors.toList());
            return baseMapper.insertBatch(res);
        } catch (Exception e) {
            log.error("关键报警导入异常", e);
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    public NodeMapVo buildNodeMap(Long equipmentId) {
        LambdaQueryWrapper<CriticalAlarmRiskEvent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(CriticalAlarmRiskEvent::getEquipmentId, equipmentId);
        List<CriticalAlarmRiskEvent> events = baseMapper.selectList(queryWrapper);
        String[] fieldName = {"reason", "bias1", "bias2", "bias3", "bias4", "eventName", "measure"};
        String[] num = {"indicatorNum", "indicatorNum1", "indicatorNum2", "indicatorNum3", "indicatorNum4", "indicatorNum6", "indicatorNum7"};
        int[] nodeType = {1, 2, 2, 2, 2, 3, 4};
        Map<Long, Map<String, Long>> nodeMap = new HashMap<>();
        String jsonStr = JSONUtil.toJsonStr(events);
        JSONArray array = JSONUtil.parseArray(jsonStr);
        List<NodeVo> nodeList = new ArrayList<>();
        List<NodeRelationVo> relationList = new ArrayList<>();
        Long nodeId = 1L;
        for (int i = 0; i < array.size(); i++) {
            JSONObject jsonObject = array.getJSONObject(i);
            Long preId = null;
            for (int f = 0; f < fieldName.length; f++) {
                String str = jsonObject.getStr(fieldName[f]);
                if (StringUtils.isNotBlank(str)) {
                    Map<String, Long> sm = nodeMap.computeIfAbsent((long) f, k -> new HashMap<>());
                    Long aLong = sm.get(str);
                    if (aLong == null) {
                        sm.put(str, nodeId);
                        NodeVo node = new NodeVo();
                        node.setNodeId(nodeId);
                        node.setNodeName(str);
                        node.setNodeType(nodeType[f]);
                        node.setCode(jsonObject.getStr(num[f]));
                        nodeList.add(node);
                        NodeRelationVo relation = new NodeRelationVo();
                        relation.setNodeFrom(preId);
                        relation.setNodeTo(nodeId);
                        relationList.add(relation);
                        preId = nodeId;
                        nodeId++;
                    } else {
                        NodeRelationVo relation = new NodeRelationVo();
                        relation.setNodeFrom(preId);
                        relation.setNodeTo(aLong);
                        relationList.add(relation);
                        preId = aLong;
                    }
                }
            }
        }
        NodeMapVo res = new NodeMapVo();
        res.setNodes(nodeList);
        relationList = relationList.stream().filter(r -> r.getNodeFrom() != null && r.getNodeTo() != null).distinct().collect(Collectors.toList());
        res.setRelations(relationList);
        return res;
    }
}
